{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/bitkira/Colab/blob/main/tutorial_notebooks/first_agent.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "!pip install git+https://github.com/EvoAgentX/EvoAgentX.git"
      ],
      "metadata": {
        "id": "Cs7EYQxwdDaf"
      },
      "id": "Cs7EYQxwdDaf",
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "!pip install PyPDF2 selenium html2text fastmcp"
      ],
      "metadata": {
        "id": "mt74D7QDdDrL"
      },
      "id": "mt74D7QDdDrL",
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "id": "706ca5aa",
      "metadata": {
        "id": "706ca5aa"
      },
      "source": [
        "# Build Your First Agent\n",
        "\n",
        "In EvoAgentX, agents are intelligent components designed to complete specific tasks autonomously. This tutorial will walk you through the essential concepts of creating and using agents in EvoAgentX:\n",
        "\n",
        "1. **Creating a Simple Agent with CustomizeAgent**: Learn how to create a basic agent with custom prompts\n",
        "2. **Working with Multiple Actions**: Create more complex agents that can perform multiple tasks\n",
        "3. **Saving and Loading Agents**: Learn how to save and load your agents\n",
        "\n",
        "By the end of this tutorial, you'll be able to create both simple and complex agents, understand how they process inputs and outputs, and know how to save and reuse them in your projects.\n",
        "\n",
        "## 1. Creating a Simple Agent with CustomizeAgent\n",
        "\n",
        "The easiest way to create an agent is using `CustomizeAgent`, which allows you to quickly define an agent with a specific prompt.  \n",
        "\n",
        "First, let's import the necessary components and setup the LLM:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "81e58527",
      "metadata": {
        "id": "81e58527"
      },
      "outputs": [],
      "source": [
        "import os\n",
        "from dotenv import load_dotenv\n",
        "from evoagentx.models import OpenAILLMConfig, BaseLLM\n",
        "from evoagentx.agents import CustomizeAgent\n",
        "\n",
        "try:\n",
        "    from google.colab import userdata\n",
        "    OPENAI_API_KEY = userdata.get(\"OPENAI_API_KEY\")\n",
        "except ImportError:\n",
        "    OPENAI_API_KEY = None\n",
        "\n",
        "if not OPENAI_API_KEY:\n",
        "    load_dotenv()\n",
        "    OPENAI_API_KEY = os.getenv(\"OPENAI_API_KEY\")\n",
        "\n",
        "# Configure LLM\n",
        "openai_config = OpenAILLMConfig(\n",
        "    model=\"gpt-4o-mini\",\n",
        "    openai_key=OPENAI_API_KEY,\n",
        "    stream=True\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f7935f59",
      "metadata": {
        "id": "f7935f59"
      },
      "source": [
        "\n",
        "Now, let's create a simple agent that prints hello world. There are two ways to create a CustomizeAgent:\n",
        "\n",
        "### Method 1: Direct Initialization\n",
        "You can directly initialize the agent with the `CustomizeAgent` class:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "6de4e524",
      "metadata": {
        "id": "6de4e524"
      },
      "outputs": [],
      "source": [
        "first_agent = CustomizeAgent(\n",
        "    name=\"FirstAgent\",\n",
        "    description=\"A simple agent that prints hello world\",\n",
        "    prompt=\"Print 'hello world'\",\n",
        "    llm_config=openai_config # specify the LLM configuration\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "98323389",
      "metadata": {
        "id": "98323389"
      },
      "source": [
        "\n",
        "### Method 2: Creating from Dictionary\n",
        "\n",
        "You can also create an agent by defining its configuration in a dictionary:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "fb35125e",
      "metadata": {
        "id": "fb35125e"
      },
      "outputs": [],
      "source": [
        "agent_data = {\n",
        "    \"name\": \"FirstAgent\",\n",
        "    \"description\": \"A simple agent that prints hello world\",\n",
        "    \"prompt\": \"Print 'hello world'\",\n",
        "    \"llm_config\": openai_config\n",
        "}\n",
        "first_agent = CustomizeAgent.from_dict(agent_data) # use .from_dict() to create an agent."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c0fa3dce",
      "metadata": {
        "id": "c0fa3dce"
      },
      "source": [
        "\n",
        "### Using the Agent\n",
        "\n",
        "Once created, you can use the agent to print hello world.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "16820fcc",
      "metadata": {
        "id": "16820fcc"
      },
      "outputs": [],
      "source": [
        "# Execute the agent without input. The agent will return a Message object containing the results.\n",
        "message = await first_agent()\n",
        "\n",
        "print(f\"Response from {first_agent.name}:\")\n",
        "print(message.content.content) # the content of a Message object is a LLMOutputParser object, where the `content` attribute is the raw LLM output."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "328b224b",
      "metadata": {
        "id": "328b224b"
      },
      "source": [
        "\n",
        "For a complete example, please refer to the [CustomizeAgent example](https://github.com/EvoAgentX/EvoAgentX/blob/main/examples/customize_agent.py).\n",
        "\n",
        "CustomizeAgent also offers other features including structured inputs/outputs and multiple parsing strategies. For detailed information, see the [CustomizeAgent documentation](../modules/customize_agent.md).\n",
        "\n",
        "## 2. Creating an Agent with Multiple Actions\n",
        "\n",
        "In EvoAgentX, you can create an agent with multiple predefined actions. This allows you to build more complex agents that can perform multiple tasks. Here's an example showing how to create an agent with `TestCodeGeneration` and `TestCodeReview` actions:\n",
        "\n",
        "### Defining Actions\n",
        "First, we need to define the actions, which are subclasses of `Action`:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f6d093ff",
      "metadata": {
        "id": "f6d093ff"
      },
      "outputs": [],
      "source": [
        "from evoagentx.agents import Agent\n",
        "from evoagentx.actions import Action, ActionInput, ActionOutput\n",
        "from pydantic import Field\n",
        "from typing import Optional\n",
        "\n",
        "# Define the CodeGeneration action inputs\n",
        "class TestCodeGenerationInput(ActionInput):\n",
        "    requirement: str = Field(description=\"The requirement for the code generation\")\n",
        "\n",
        "# Define the CodeGeneration action outputs\n",
        "class TestCodeGenerationOutput(ActionOutput):\n",
        "    code: str = Field(description=\"The generated code\")\n",
        "\n",
        "# Define the CodeGeneration action\n",
        "class TestCodeGeneration(Action):\n",
        "\n",
        "    def __init__(\n",
        "        self,\n",
        "        name: str=\"TestCodeGeneration\",\n",
        "        description: str=\"Generate code based on requirements\",\n",
        "        prompt: str=\"Generate code based on requirements: {requirement}\",\n",
        "        inputs_format: ActionInput=None,\n",
        "        outputs_format: ActionOutput=None,\n",
        "        **kwargs\n",
        "    ):\n",
        "        inputs_format = inputs_format or TestCodeGenerationInput\n",
        "        outputs_format = outputs_format or TestCodeGenerationOutput\n",
        "        super().__init__(\n",
        "            name=name,\n",
        "            description=description,\n",
        "            prompt=prompt,\n",
        "            inputs_format=inputs_format,\n",
        "            outputs_format=outputs_format,\n",
        "            **kwargs\n",
        "        )\n",
        "\n",
        "    def execute(self, llm: Optional[BaseLLM] = None, inputs: Optional[dict] = None, sys_msg: Optional[str]=None, return_prompt: bool = False, **kwargs) -> TestCodeGenerationOutput:\n",
        "        action_input_attrs = self.inputs_format.get_attrs() # obtain the attributes of the action input\n",
        "        action_input_data = {attr: inputs.get(attr, \"undefined\") for attr in action_input_attrs}\n",
        "        prompt = self.prompt.format(**action_input_data) # format the prompt with the action input data\n",
        "        output = llm.generate(\n",
        "            prompt=prompt,\n",
        "            system_message=sys_msg,\n",
        "            parser=self.outputs_format,\n",
        "            parse_mode=\"str\" # specify how to parse the output\n",
        "        )\n",
        "        if return_prompt:\n",
        "            return output, prompt\n",
        "        return output\n",
        "\n",
        "\n",
        "# Define the CodeReview action inputs\n",
        "class TestCodeReviewInput(ActionInput):\n",
        "    code: str = Field(description=\"The code to be reviewed\")\n",
        "    requirements: str = Field(description=\"The requirements for the code review\")\n",
        "\n",
        "# Define the CodeReview action outputs\n",
        "class TestCodeReviewOutput(ActionOutput):\n",
        "    review: str = Field(description=\"The review of the code\")\n",
        "\n",
        "# Define the CodeReview action\n",
        "class TestCodeReview(Action):\n",
        "    def __init__(\n",
        "        self,\n",
        "        name: str=\"TestCodeReview\",\n",
        "        description: str=\"Review the code based on requirements\",\n",
        "        prompt: str=\"Review the following code based on the requirements:\\n\\nRequirements: {requirements}\\n\\nCode:\\n{code}.\\n\\nYou should output a JSON object with the following format:\\n```json\\n{{\\n'review': '...'\\n}}\\n```\",\n",
        "        inputs_format: ActionInput=None,\n",
        "        outputs_format: ActionOutput=None,\n",
        "        **kwargs\n",
        "    ):\n",
        "        inputs_format = inputs_format or TestCodeReviewInput\n",
        "        outputs_format = outputs_format or TestCodeReviewOutput\n",
        "        super().__init__(\n",
        "            name=name,\n",
        "            description=description,\n",
        "            prompt=prompt,\n",
        "            inputs_format=inputs_format,\n",
        "            outputs_format=outputs_format,\n",
        "            **kwargs\n",
        "        )\n",
        "\n",
        "    def execute(self, llm: Optional[BaseLLM] = None, inputs: Optional[dict] = None, sys_msg: Optional[str]=None, return_prompt: bool = False, **kwargs) -> TestCodeReviewOutput:\n",
        "        action_input_attrs = self.inputs_format.get_attrs()\n",
        "        action_input_data = {attr: inputs.get(attr, \"undefined\") for attr in action_input_attrs}\n",
        "        prompt = self.prompt.format(**action_input_data)\n",
        "        output = llm.generate(\n",
        "            prompt=prompt,\n",
        "            system_message=sys_msg,\n",
        "            parser=self.outputs_format,\n",
        "            parse_mode=\"json\" # specify how to parse the output\n",
        "        )\n",
        "        if return_prompt:\n",
        "            return output, prompt\n",
        "        return output"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "085eb579",
      "metadata": {
        "id": "085eb579"
      },
      "source": [
        "\n",
        "From the above example, we can see that in order to define an action, we need to:\n",
        "\n",
        "1. Define the action inputs and outputs using `ActionInput` and `ActionOutput` classes\n",
        "2. Create an action class that inherits from `Action`\n",
        "3. Implement the `execute` method which formulates the prompt with the action input data and uses the LLM to generate output, and specify how to parse the output using `parse_mode`.\n",
        "\n",
        "### Defining an Agent\n",
        "\n",
        "Once we have defined the actions, we can create an agent by adding the actions to it:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "4be1a7db",
      "metadata": {
        "id": "4be1a7db"
      },
      "outputs": [],
      "source": [
        "# Initialize the LLM\n",
        "openai_config = OpenAILLMConfig(model=\"gpt-4o-mini\", openai_key=OPENAI_API_KEY)\n",
        "\n",
        "# Define the agent\n",
        "developer = Agent(\n",
        "    name=\"Developer\",\n",
        "    description=\"A developer who can write code and review code\",\n",
        "    actions=[TestCodeGeneration(), TestCodeReview()],\n",
        "    llm_config=openai_config\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "91c62988",
      "metadata": {
        "id": "91c62988"
      },
      "source": [
        "\n",
        "### Executing Different Actions\n",
        "\n",
        "Once you've created an agent with multiple actions, you can execute specific actions:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "b53fa1e6",
      "metadata": {
        "id": "b53fa1e6"
      },
      "outputs": [],
      "source": [
        "# List all available actions on the agent\n",
        "actions = developer.get_all_actions()\n",
        "print(f\"Available actions of agent {developer.name}:\")\n",
        "for action in actions:\n",
        "    print(f\"- {action.name}: {action.description}\")\n",
        "\n",
        "# Generate some code using the CodeGeneration action\n",
        "generation_result = developer.execute(\n",
        "    action_name=\"TestCodeGeneration\", # specify the action name\n",
        "    action_input_data={\n",
        "        \"requirement\": \"Write a function that returns the sum of two numbers\"\n",
        "    }\n",
        ")\n",
        "\n",
        "# Access the generated code\n",
        "generated_code = generation_result.content.code\n",
        "print(\"Generated code:\")\n",
        "print(generated_code)\n",
        "\n",
        "# Review the generated code using the CodeReview action\n",
        "review_result = developer.execute(\n",
        "    action_name=\"TestCodeReview\",\n",
        "    action_input_data={\n",
        "        \"requirements\": \"Write a function that returns the sum of two numbers\",\n",
        "        \"code\": generated_code\n",
        "    }\n",
        ")\n",
        "\n",
        "# Access the review results\n",
        "review = review_result.content.review\n",
        "print(\"\\nReview:\")\n",
        "print(review)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "91683e62",
      "metadata": {
        "id": "91683e62"
      },
      "source": [
        "\n",
        "This example demonstrates how to:\n",
        "1. List all available actions on an agent\n",
        "2. Generate code using the TestCodeGeneration action\n",
        "3. Review the generated code using the TestCodeReview action\n",
        "4. Access the results from each action execution\n",
        "\n",
        "For a complete working example, please refer to the [Agent example](https://github.com/EvoAgentX/EvoAgentX/blob/main/examples/agent_with_multiple_actions.py).\n",
        "\n",
        "\n",
        "## 3. Saving and Loading Agents\n",
        "\n",
        "You can save an agent to a file and load it later:\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "6f02bc52",
      "metadata": {
        "id": "6f02bc52"
      },
      "outputs": [],
      "source": [
        "# Save the agent to a file\n",
        "developer.save_module(\"examples/output/developer.json\") # ignore the LLM config to avoid saving the API key\n",
        "\n",
        "# Load the agent from a file\n",
        "developer = Agent.from_file(\"examples/output/developer.json\", llm_config=openai_config)"
      ]
    }
  ],
  "metadata": {
    "language_info": {
      "name": "python"
    },
    "colab": {
      "provenance": [],
      "include_colab_link": true
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}